package be.dch.util;

import java.io.IOException;
import java.io.OutputStream;

import javax.swing.JTextArea;

import org.apache.log4j.Layout;
import org.apache.log4j.WriterAppender;

/**
 * ConsoleAppender appends log events to <code>System.out</code> or
 * <code>System.err</code> using a layout specified by the user. The default
 * target is <code>System.out</code>.
 * 
 */
public class Log4jTextAreaAppender extends WriterAppender {

    /**
     * Determines if the appender honors reassignments of System.out or
     * System.err made after configuration.
     */
    private boolean follow = false;
    private transient JTextArea textArea;
    private final transient int defaultRows;

    /**
     * Constructs an unconfigured appender.
     */
    public Log4jTextAreaAppender() {
	super();
	this.textArea = new JTextArea();
	this.defaultRows = 15;
    }

    /**
     * Creates a configured appender.
     * 
     * @param layout
     *            layout, may not be null.
     * @param target
     *            target, either "System.err" or "System.out".
     */
    public Log4jTextAreaAppender(final Layout layout, final JTextArea area) {
	super();
	this.setLayout(layout);
	this.activateOptions();
	this.textArea = area;
	this.defaultRows = area.getRows();
    }

    /**
     * Sets whether the appender honors reassignments of System.out or
     * System.err made after configuration.
     * 
     * @param newValue
     *            if true, appender will use value of System.out or System.err
     *            in force at the time when logging events are appended.
     * @since 1.2.13
     */
    public final void setFollow(final boolean newValue) {
	this.follow = newValue;
    }

    /**
     * Gets whether the appender honors reassignments of System.out or
     * System.err made after configuration.
     * 
     * @return true if appender will use value of System.out or System.err in
     *         force at the time when logging events are appended.
     * @since 1.2.13
     */
    public final boolean isFollow() {
	return this.follow;
    }

    /**
     * Prepares the appender for use.
     */
    @Override
    public final void activateOptions() {
	this.setWriter(this.createWriter(new OutputPanelStream()));

	super.activateOptions();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected final void closeWriter() {
	if (this.follow) {
	    super.closeWriter();
	}
    }

    public void setTextArea(final JTextArea textArea) {
	this.textArea = textArea;
    }

    /**
     * An implementation of OutputStream that redirects to the current
     * System.err.
     * 
     */
    private class OutputPanelStream extends OutputStream {
	private transient int rows = 0;

	@Override
	public void close() {
	    // No need to close anything.
	}

	@Override
	public void flush() {
	    // No need to flush anything.

	}

	@Override
	public void write(final byte[] b) throws IOException {
	    this.write(b, 0, b.length);
	}

	@Override
	public void write(final byte[] b, final int off, final int len) throws IOException {
	    this.write(new String(b, off, len));
	}

	@Override
	public void write(final int b) throws IOException {
	    this.write(String.valueOf((char) b));
	}

	private void write(final String string) {
	    if (this.rows > Log4jTextAreaAppender.this.defaultRows) {
		Log4jTextAreaAppender.this.textArea.setRows(Log4jTextAreaAppender.this.textArea.getRows() + 1);
	    }
	    this.rows++;
	    Log4jTextAreaAppender.this.textArea.append(string);
	}
    }

}
